﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using NetBay.Core.BusinessEntities.ActiveDirectory.Objects;
using Rio.Manager.Business.Repository;
using NetBay.Core.BusinessEntities.ActiveDirectory;
using System.DirectoryServices;
using System.Collections.ObjectModel;
using NetBay.Core.Tools;
using System.Transactions;
using System.ServiceModel;
using System.Configuration;

namespace Rio.Applications.Web.Services.AD
{
    [ServiceBehavior(MaxItemsInObjectGraph = 2147483647)]
    [EnableClientAccess(RequiresSecureEndpoint = false)]
    public class AdDomainService : DomainService
    {
        /// <summary>Override Submit.</summary>
        /// <param name="changeSet"></param>
        /// <returns></returns>
        public override bool Submit(ChangeSet changeSet)
        {
            //Variable initialization.
            bool l_bReturnValue;

            try
            {
                //Transaction Scope.
                using (var l_oTransactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
                {
                    l_bReturnValue = base.Submit(changeSet);
                    if (!ChangeSet.HasError)
                    {
                        l_oTransactionScope.Complete();
                    }
                }
            }
            catch (Exception ex)
            {
                //Error Log.
                NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.Submit Exception [" + ex.Message + "]");
                throw new Exception(ex.Message);
            }

            //Return result.
            return l_bReturnValue;
        }

        #region Get

        /// <summary>
        /// Gets all rio and Ad Correspondence.
        /// </summary>
        /// <returns></returns>
        [Query(HasSideEffects = true)]
        public IEnumerable<AdInformationUserRio> GetAllRioADCorrespondence()
        {
            //Variable Initialization.
            List<AdInformationUserRio> l_oReturnRioAndADCorrespondenceSave;

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("ActiveDirectoryDS.GetAllRioADCorrespondence (No parameters)...");

            try
            {
                l_oReturnRioAndADCorrespondenceSave = ActiveDirectoryRepository.GetAllRioADCorrespondence();
            }
            catch (Exception ex)
            {
                //Error.
                NetBay.Core.Log.LogManager.Instance().LogError("ActiveDirectoryDS.GetAllRioADCorrespondence Exception [" + ex.Message + "]");
                l_oReturnRioAndADCorrespondenceSave = null;
            }
            return l_oReturnRioAndADCorrespondenceSave;
        }

        /// <summary>
        /// Returns all Adconfig from the configuration file
        /// </summary>
        /// <returns>An AdConfig object containing all Ad config declared in the web.config file</returns>
        [Query(HasSideEffects = true)]
        public IEnumerable<AdConfig> GetAdConfigList()
        {
            //Variable initialization.
            var l_oAdConfigs = new List<AdConfig>();

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.GetAdConfigList (No parameters)...");

            //Retrieves all Ad config.
            try
            {
                l_oAdConfigs.Add(ConfigurationManager.GetSection("AdConfig") as AdConfig);
            }
            catch (Exception ex)
            {
                //Error log.
                NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.GetAdConfigList Exception [" + ex.Message + "]");
            }

            //Return value.
            return l_oAdConfigs;
        }

        /// <summary>
        /// Returns an Ad configuration structure 
        /// </summary>
        /// <param name="p_sAdConfigName">The name of a configuration</param>
        /// <returns>An AdConfig structure object containing all Active directory connection informations</returns>
        [Query(HasSideEffects = true)]
        public IEnumerable<AdStructureItem> GetAdConfig(string p_sAdConfigName)
        {
            //Variable initialization.
            var l_oAdConfigStructures = new List<AdStructureItem>();

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.GetAdConfig (p_sAdConfigName <" + p_sAdConfigName + ">)...");

            //Retrieve one structure.
            try
            {
                var l_oAdConfig = ConfigurationManager.GetSection("AdConfig") as AdConfig;

                if (l_oAdConfig != null)
                {
                    foreach (AdStructureItem uneStruct in l_oAdConfig.LstAdStructureItem)
                    {
                        if (uneStruct.AdName != p_sAdConfigName) continue;
                        l_oAdConfigStructures.Add(uneStruct);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                //Error log.
                NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.GetAdConfig Exception [" + ex.Message + "]");
            }

            //Return Value.
            return l_oAdConfigStructures;
        }

        /// <summary>
        /// Returns Ad User Column structure.
        /// </summary>
        /// <param name="p_oAdStructure">the declaratin of an ad</param>
        /// <returns>a list of string represeting the column structure of a user in this ad</returns> 
        [Invoke(HasSideEffects = true)]
        public IEnumerable<string> GetAdUserColumnStructure(AdStructureItem p_oAdStructure)
        {
            //Variable initialization.
            List<string> l_sLstReturnAdColumn = new List<string>();
            
            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.GetAdUserColumnStructure (p_oAdStructure.AdName <" + p_oAdStructure.AdName + ">, p_oAdStructure.AdLogin<" + p_oAdStructure.AdLogin + ">)...");

            //Retrieves AD column.
            try
            {
                Connection l_oConnection = new Connection(p_oAdStructure);

                List<string> l_oRet = new List<string>();
                DirectorySearcher l_oSearchObj = null;
                try
                {
                    l_oSearchObj = new DirectorySearcher(
                            l_oConnection.GetDirectoryEntry(), string.Format("(name={0})", p_oAdStructure.AdLogin)
                                );

                    var l_oObj = l_oSearchObj.FindOne();

                    if (l_oObj == null)
                    {
                        throw new Exception(string.Format("Impossible de trouver l'identifiant {0} dans Active Directory", p_oAdStructure.AdLogin));
                    }

                    var l_oResultPropertyCollection = l_oObj.Properties;

                    if (l_oResultPropertyCollection.PropertyNames != null)
                    {
                        var l_oTempArray = new string[l_oResultPropertyCollection.PropertyNames.Count];
                        if (l_oObj.Properties.PropertyNames != null)
                            l_oObj.Properties.PropertyNames.CopyTo(l_oTempArray, 0);
                        l_oRet.AddRange(l_oTempArray);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Exception in AdDomainService.GetAdUserColumnStructure", ex);
                }
                finally
                {
                    if (l_oSearchObj != null)
                        l_oSearchObj.Dispose();
                }
                l_sLstReturnAdColumn.AddRange(l_oRet);
            }
            catch (Exception ex)
            {
                //Error.
                NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.GetAdUserColumnStructure Exception [" + ex.Message + "]");
                l_sLstReturnAdColumn = null;
            }

            //Return value.
            return l_sLstReturnAdColumn;
        }

        /// <summary>
        /// Returns All Ad Groups
        /// </summary>
        /// <param name="p_oAdStructureSerialize">the declaratin of an ad</param>
        /// <returns>Returns a list of adGroup from an ad</returns>
        [Query(HasSideEffects = true)]
        public IEnumerable<AdGroup> GetAdGroupList(string p_oAdStructureSerialize)
        {
            //Variable initialization.
            List<AdGroup> ReturnGroups = new List<AdGroup>();

            //Deserialize AdStructureItem object.
            AdStructureItem p_oAdStructure = SerializationHelper.Deserialize<AdStructureItem>(p_oAdStructureSerialize);

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.GetAdGroupList (p_oAdStructure.AdName <" + p_oAdStructure.AdName + ">, p_oAdStructure.AdLogin<" + p_oAdStructure.AdLogin + ">)...");

            try
            {
                //Variable initialization.
                Connection l_oConnection = new Connection(p_oAdStructure);

                //Filter on Ad Group.
                DirectorySearcher searchObj = new DirectorySearcher(l_oConnection.GetDirectoryEntry()) { Filter = "(&(objectCategory=group))" };
                var objs = searchObj.FindAll();

                foreach (SearchResult obj in objs)
                {
                    //Variable initialization.
                    AdGroup result = new AdGroup();
                    try
                    {
                        //Fill AdGroup Information
                        result.GuidOfAdObject = new Guid((byte[])obj.Properties["objectGUID"][0]);                   
                        result.Name = obj.Properties["name"][0].ToString();                   
                        result.DisplayName = result.InformationName;

                        ReturnGroups.Add(result);
                    }
                    catch (Exception ex)
                    {
                        //Error Log.
                        NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.GetAdGroupList Exception [" + ex.Message + "]");
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Exceptin in AdDomainService.GetAdGroupList (p_oAdStructure.AdName <" + p_oAdStructure.AdName + ">, p_oAdStructure.AdLogin<" + p_oAdStructure.AdLogin + ">)...", ex);
            }

            //Return value.
            return ReturnGroups;
        }

        /// <summary>Returns All Ad users.</summary>
        /// <param name="p_oAdStructureSerialize">the declaratin of an ad</param>
        /// <returns>Returns a list of adUser from an ad</returns>
        [Query(HasSideEffects = true)]        
        public IEnumerable<AdUser> GetAdUserList(string p_oAdStructureSerialize)
        {
            //Variable initialization.
            List<AdUser> ReturnUsers = new List<AdUser>();

            //Deserialize AdStructureItem object.
            AdStructureItem p_oAdStructure = SerializationHelper.Deserialize<AdStructureItem>(p_oAdStructureSerialize);

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.GetAdUserList (p_oAdStructure.AdName <" + p_oAdStructure.AdName + ">, p_oAdStructure.AdLogin<" + p_oAdStructure.AdLogin + ">)...");

            try
            {
                //Variable Initialization.
                Connection l_oConnection = new Connection(p_oAdStructure);
                DirectorySearcher searchObj = new DirectorySearcher(l_oConnection.GetDirectoryEntry());

                //Filter on Ad user.
                searchObj.Filter = "(&(objectCategory=user))";
                var objs = searchObj.FindAll();

                //Retrieves Ad Information to build 
                List<AdInformationUserRio> l_oAdMappingInformation = ActiveDirectoryRepository.GetAllRioADCorrespondence();

                foreach (SearchResult obj in objs)
                {
                    //Variable initialization.
                    AdUser _OneResult = new AdUser();

                    try
                    {
                        //Fill AdUser informations.
                        if (Convert.ToBoolean((int)obj.GetDirectoryEntry().Properties["userAccountControl"].Value & 0x0002))
                        {
                            //User is inactif.
                            continue;
                        }

                        _OneResult.GuidOfAdObject = new Guid((byte[])obj.GetDirectoryEntry().Properties["objectGUID"].Value);

                        _OneResult.Name = obj.GetDirectoryEntry().Properties[(from l_oAdMappingInformationByType in l_oAdMappingInformation where l_oAdMappingInformationByType.Type == UserRioInformationType.LastName select l_oAdMappingInformationByType.Value).FirstOrDefault().ToString()].Value.ToString();
                        _OneResult.FirstName = obj.GetDirectoryEntry().Properties[(from l_oAdMappingInformationByType in l_oAdMappingInformation where l_oAdMappingInformationByType.Type == UserRioInformationType.FirstName select l_oAdMappingInformationByType.Value).FirstOrDefault().ToString()].Value.ToString();
                        _OneResult.Login = obj.GetDirectoryEntry().Properties[(from l_oAdMappingInformationByType in l_oAdMappingInformation where l_oAdMappingInformationByType.Type == UserRioInformationType.Login select l_oAdMappingInformationByType.Value).FirstOrDefault().ToString()].Value.ToString();
                        _OneResult.Email = obj.GetDirectoryEntry().Properties[(from l_oAdMappingInformationByType in l_oAdMappingInformation where l_oAdMappingInformationByType.Type == UserRioInformationType.Email select l_oAdMappingInformationByType.Value).FirstOrDefault().ToString()].Value.ToString();

                        _OneResult.DisplayName = _OneResult.InformationName;

                        ReturnUsers.Add(_OneResult);
                    }
                    catch (Exception ex)
                    {
                        //Error Log.
                        NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.GetAdUserList Exception [" + ex.Message + "]");

                        //Clear user list.
                        ReturnUsers.Clear();
                        
                        //Information Aduser in error.
                        string l_oAdUserErrorInformationToDisplay = "distinguishedName";
                        AdUser l_oErrorResult = new AdUser();
                        l_oErrorResult.UserAdError = true;
                        if (ConfigurationManager.AppSettings.Get("AdUserListError") != null)
                        {
                            l_oAdUserErrorInformationToDisplay = ConfigurationManager.AppSettings.Get("AdUserListError");
                        }
                        l_oErrorResult.UserDisplayAdError = obj.Properties[l_oAdUserErrorInformationToDisplay][0].ToString();
                        
                        ReturnUsers.Add(l_oErrorResult);

                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("AdDomainService.GetAdUserList (p_oAdStructure.AdName <" + p_oAdStructure.AdName + ">, p_oAdStructure.AdLogin<" + p_oAdStructure.AdLogin + ">)...", ex);
            }

            return ReturnUsers;
        }

        /// <summary>
        /// Returns all mapd objects
        /// </summary>
        /// <param name="p_lGroupId">the identifier of a Rio Group</param>
        /// <param name="p_eAdObjectType">the type of object to retur</param>
        /// <returns>a list of AdObject</returns>
        [Query(HasSideEffects = true)]
        public IEnumerable<AdObject> GetAdMappedObject(long p_lGroupId, AdObjectType p_eAdObjectType)
        {
            //Variable initialization.
            List<AdObject> l_oRet = new List<AdObject>();

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.GetAdMappedObject (p_lGroupId <" + p_lGroupId.ToString() + ">, AdObjectType<" + (int)p_eAdObjectType + ">)...");

            try
            {
                l_oRet = ActiveDirectoryRepository.GetAdMappedObject(p_lGroupId, p_eAdObjectType);
            }
            catch (Exception ex)
            {
                //Log error.
                NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.GetAdMappedObject (p_lGroupId <" + p_lGroupId.ToString() + ">, AdObjectType<" + (int)p_eAdObjectType + ">) Exception : " + ex.Message);
            }

            //return value.
            return l_oRet;
        }

        /// <summary>
        /// Gets ad name mapped.
        /// </summary>
        /// <returns></returns>
        [Invoke(HasSideEffects = true)]
        public string GetAdNameMapped()
        {
            //Variable Initialization.
            string l_sReturnAdNameMapped;

            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("ActiveDirectoryDS.GetAdNameMapped (No parameters)...");

            try
            {
                l_sReturnAdNameMapped = ActiveDirectoryRepository.GetAdNameMapped();
            }
            catch (Exception ex)
            {
                //Error.
                NetBay.Core.Log.LogManager.Instance().LogError("ActiveDirectoryDS.GetAdNameMapped Exception [" + ex.Message + "]");
                l_sReturnAdNameMapped = null;
            }
            return l_sReturnAdNameMapped;
        }

        #endregion Get

        #region Add

        [Insert]
        public void AddAdMapping(AdObject p_oAdMapping)
        {
            //Log.
            NetBay.Core.Log.LogManager.Instance().LogDebug("AdDomainService.AddAdMapping (p_oAdMapping <" + p_oAdMapping + ">, Mapping p_oAdMapping.TypeOfAdObject <" + p_oAdMapping.TypeOfAdObject.ToString() + ">, p_oAdMapping.DisplayName <" + p_oAdMapping.DisplayName + ">)...");

            try
            {
                //Object test to map (User or Group).
                switch (p_oAdMapping.TypeOfAdObject)
                {
                    case AdObjectType.All:
                        //Log.
                        NetBay.Core.Log.LogManager.Instance().LogDebug("Object Ad Type 'All' isn't support by RIO.");
                        break;
                    case AdObjectType.Group:
                        ActiveDirectoryRepository.AddAdGroupMapping((AdGroup)p_oAdMapping);
                        break;

                    case AdObjectType.User:
                        ActiveDirectoryRepository.AddAdUserMapping((AdUser)p_oAdMapping);
                        break;

                    default:
                        //Log.
                        NetBay.Core.Log.LogManager.Instance().LogDebug("Object Ad Type isn't support by RIO (case default).");
                        break;
                }
            }
            catch (Exception ex)
            {
                //Error Log.
                NetBay.Core.Log.LogManager.Instance().LogError("AdDomainService.AddAdMapping Exception [" + ex.Message + "]");
                throw new Exception(ex.Message);
            }
        }

        #endregion Add

        #region Update

        /// <summary>
        /// Update all rio and Ad Correspondence.
        /// </summary>
        /// <returns></returns>
        [Update]
        public void UpdateRioADCorrespondence(AdInformationUserRio p_oNewValueCorrespondence)
        {
           ActiveDirectoryRepository.UpdateRioADCorrespondence(new ObservableCollection<AdInformationUserRio>() { p_oNewValueCorrespondence });          
        }

        #endregion Update

        #region Delete

        [Delete]
        public void DeleteAdMappedObject(AdObject p_oAdMappedToDelete)
        {
            ActiveDirectoryRepository.DeleteAdMappedObject(p_oAdMappedToDelete);
        }       

        #endregion
    }
}